const PENDING = 'PENDING'
const REJECTED = 'REJECTED'
const RESOLVED = 'RESOLVED'

function MyPromise(fn) {
  this.status = PENDING
  this.value = null
  this.reason = null
  this.resolvedFns = []
  this.rejectedFns = []

  const resolve = (value) => {
    this.value = value
    setTimeout(() => {
      if (this.status === PENDING) {
        this.status = RESOLVED
        this.resolvedFns.forEach((fn) => fn(this.value))
      }
    })
  }

  const reject = (reason) => {
    this.reason = reason
    setTimeout(() => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.rejectedFns.forEach((fn) => fn(this.reason))
      }
    })
  }

  try {
    fn(resolve, reject)
  } catch (e) {
    reject(e)
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  onResolved = typeof onResolved === 'function' ? onResolved : () => {}
  onRejected = typeof onRejected === 'function' ? onRejected : () => {}

  return new Promise((resolve, reject) => {
    function handle(callback) {
      try {
        const x = callback(this.value)
        if (x instanceof MyPromise) {
          x.then(resolve, reject)
        } else {
          resolve(x)
        }
      } catch (e) {
        reject(e)
      }
    }
    if (this.status === RESOLVED) {
      setTimeout(() => {
        handle(onResolved)
      })
    }

    if (this.status === REJECTED) {
      setTimeout(() => {
        handle(onRejected)
      })
    }

    if (this.status === PENDING) {
      this.resolvedFns.push(function () {
        handle(onResolved)
      })
      this.rejectedFns.push(function () {
        handle(onRejected)
      })
    }
  })
}
